一個recyclerview裡可以再裝recyclerview,俄羅斯娃娃啦,懂?
今天要做的是一個縱向rview裡裝著一個橫向滑動的rview,縱向rview裝有大卡片及banner卡片兩種viewtype,橫向滑動rview則只裝有小卡片一種viewtype。我們會由adapter中的viewtype來判別要回傳哪一種viewholder。
以下會用rview或r_view來稱呼recyclerview。
圖片來源:CCC創作集
先放一個recyclerview,這是縱向(外層)rview
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/r_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
建一個xml檔,取名container_horiz,當作橫向(內層)rview的container(容器),然後裡面一樣放一個recyclerview。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/r_view_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
寫一個dataModel,定義資料格式。
data class Item(
val img:Int,
val title:String,
val content:String,
val viewType:Int
)
//viewType:
// 1->小卡片
// 2->大卡片
// 3->banner卡片
val mainList = arrayListOf<Item>()
val horizList = arrayListOf<Item>()
//產生橫向(內層)rview資料
fun hDataGenerate(){
for(i in 0..5){
horizList.add(Item(R.drawable.work_smallcard,"滑動作品小卡片","文字介紹",1))
horizList.add(Item(R.drawable.subject_smallcard,"滑動專題小卡片","文字介紹",1))
}
}
//產生縱向(外層)rview的資料
fun dataGenerate(){
for(i in 0..3){
//banner卡片不需要顯示title跟content
mainList.add(Item(R.drawable.bn_05,"","",3))
mainList.add(Item(R.drawable.work_smallcard,"作品小卡片","文字介紹",1))
mainList.add(Item(R.drawable.work_bigcard,"作品大卡片","文字介紹",2))
}
}
建一個layout檔,取名item_slidecard,裡面寫橫向rview的項目模板。
<ImageView
android:id="@+id/imgv_slidecard"
android:layout_width="match_parent"
android:layout_height="0dp"
/>
<TextView
android:id="@+id/tv_title_slidecard"
android:layout_width="0dp"
android:layout_height="30dp"
/>
建一個layout檔,取名item_bigcard,裡面寫縱向rview的項目模板。(這裡就不把每種viewtype都寫一遍了,僅寫大卡片作為代表)
<ImageView
android:id="@+id/imgv_bigcard"
android:layout_width="match_parent"
android:layout_height="0dp"
/>
<TextView
android:id="@+id/tv_title_bigcard"
android:layout_width="match_parent"
android:layout_height="40dp"
<TextView
android:id="@+id/tv_content_bigcard"
android:layout_width="match_parent"
android:layout_height="40dp"
/>
幫橫向rview寫一個adapter,取名HorizontalAdapter,沒啥特別就是一個普通的adapter。
class HorizontalAdapter:RecyclerView.Adapter<HorizontalAdapter.mViewHolder>() {
var localList = arrayListOf<Item>()
inner class mViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
val img = itemView.imgv_slidecard
val title = itemView.tv_title_slidecard
fun bind(item:Item){
img.setImageResource(item.img)
title.text = item.title
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): mViewHolder {
val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(R.layout.item_slidecard,parent,false)
return mViewHolder(itemView)
}
override fun getItemCount() = localList.size
override fun onBindViewHolder(holder: mViewHolder, position: Int) {
holder.bind(localList[position])
}
//更新資料用
fun updateList(list:ArrayList<Item>){
localList = list
}
}
重點來了。幫縱向rview寫一個adapter,取名MainAdapter
class MainAdapter(val context:Context)
:RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var emptyList = arrayListOf<Item>()
//把水平rview元件拉進來
inner class horizontalViewHolder(itemView:View)
:RecyclerView.ViewHolder(itemView){
val rView = itemView.r_view_horizantal
}
//這裡跟原本一樣,把大卡片項目模板的元件們拉進來
inner class bigCardViewHolder(itemView:View)
:RecyclerView.ViewHolder(itemView){
val img = itemView.imgv_bigcard
val title = itemView.tv_title_bigcard
val content = itemView.tv_content_bigcard
fun bind(item:Item){
img.setImageResource(item.img)
title.text = item.title
content.text = item.content
}
}
override fun getItemViewType(position: Int): Int {
return emptyList[position].viewType
}
override fun onCreateViewHolder(parent:ViewGroup,viewType: Int)
: RecyclerView.ViewHolder {
return when(viewType){
0->{
//這裡把container載入進來,做出巢狀recyclerview
val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(R.layout.container_horiz,parent,false)
//設定水平滑動的recyclerview
//因為綁adapter跟layoutmanager這些是一次性的東西,如果寫在onBindViewHolder,就會在使用者每一次下滑捲動 系統更新項目資料的時候一直重複呼叫!
val adapter = HorizontalAdapter()
adapter.updateList(horizList)
val linearlayout = LinearLayoutManager(context)
linearlayout.orientation = RecyclerView.HORIZONTAL
HorizontalViewHolder(itemView).rView.layoutManager = linearlayout
HorizontalViewHolder(itemView).rView.adapter = adapter
HorizontalViewHolder(itemView)
}
1->{
//載入大卡片的項目模板
val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(R.layout.item_bigcard,parent,false)
bigCardViewHolder(itemView)
}
2->{
........
}
else->{
.......
}
}
}
override fun getItemCount() = emptyList.size
override fun onBindViewHolder(
holder:RecyclerView.ViewHolder,
position: Int) {
when(holder ){
is horizontalViewHolder->{}
is bigCardViewHolder -> holder.bind(emptyList[position])
}
}
//更新資料用
fun updateList(list:ArrayList<Item>){
emptyList = list
}
}
回到MainActivity去設定縱向rview的adapter及layoutManager
class MainActivity : AppCompatActivity() {
val adapter = MainAdapter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//產生資料
dataGenerate()
hDataGenerate()
//設定縱向rview
adapter.updateList(mainList)
r_view.adapter = adapter
r_view.layoutManager = LinearLayoutManager(this)
adapter.notifyDataSetChanged()
}
}
Done.